/*
 * Copyright (c) 2024-2025 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <core/endpoint.h>
#include <core/schedule.h>
#include <caps/capability.h>
#include <caps/endpoint.h>
#include <uapi/errors.h>
#include <uapi/ipcbuffer.h>
#include <uapi/ipcops.h>
#include <libc/errno.h>

static inline void cap_ep_set_obj(capability_t *cap, endpoint_t *obj)
{
    assert(((unsigned long)obj & 0x3) == 0);

    cap_set_long_info(cap, (unsigned long)obj);
}

static inline unsigned int cap_ep_get_id(capability_t *cap)
{
    return (cap_get_short_info(cap) >> CAP_EP_ATTR_BIT_ID_BEGIN) & lowbitsmask(CAP_EP_ATTR_BIT_ID_WIDTH);
}

static inline void cap_ep_set_id(capability_t *cap, unsigned int id)
{
    cap_set_short_info(cap, id);
}

void cap_ep_init(capability_t *cap, endpoint_t *obj, unsigned int id)
{
    endpoint_get(obj);
    cap_basic_init(cap, CAP_EP);
    cap_ep_set_obj(cap, obj);
    cap_ep_set_id(cap, id);
}

void cap_ep_dispatcher(capability_t *cap, unsigned long tag, unsigned long m0, unsigned long m1, unsigned long m2,
                       unsigned long m3, unsigned long m4, unsigned long m5)
{
    endpoint_t *ep = cap_ep_get_obj(cap);

    if (spin_try_lock(&ep->lock)) {
        switch (msgtag_get_op(tag)) {
            case CAP_EP_OP_SEND:
                tag = msgtag_set_id(tag, cap_ep_get_id(cap));
                endpoint_send(ep, tag, m0, m1, current());
                break;

            case CAP_EP_OP_RECV:
                endpoint_receive(ep, tag, current());
                break;

            case CAP_EP_OP_CALL:
                tag = msgtag_set_id(tag, cap_ep_get_id(cap));
                endpoint_call(ep, tag, m0, m1, current());
                break;

            case CAP_EP_OP_REPLY:
                endpoint_reply(ep, tag, m0, m1, current());
                break;

            case CAP_EP_OP_CANCEL:
                endpoint_cancel(ep);
                task_set_retcode(current(), ESUCCESS);
                break;

            default:
                task_set_retcode(current(), ENOSYS);
                break;
        }
        spin_unlock(&ep->lock);
    } else {
        task_set_retcode(current(), EISCONN);
    }
}
